<?php

    #-------------------------------------------------------------#
	# This file is part of the Tekuna MVC Microframework for PHP. #
	# (c) 2009 Niels Jäckel <niels.jaeckel@tekuna.org>            #
	# Please visit http://tekuna.org/ for more information.       #
	#                                                             #
	# This software is licensed under the MIT License. Please     #
	# view the LICENSE file for the full text of this license.    #
    #-------------------------------------------------------------#


	Tekuna :: load('org.tekuna.core.persistence.TextDB');


	class TextDBTest extends AbstractTekunaTestCase {


		protected function isWindows() {

			return (stripos(php_uname('s'), 'Windows') !== FALSE);
		}


		public function testFileLifeCycle() {

			// get TextDB directory
			$sTextDBDir = APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'textdb' . DIRECTORY_SEPARATOR;

			// before the instantiation the textfile must not exist
			clearstatcache();
			$this -> assertFalse(file_exists($sTextDBDir . 'TestTable.textdb'));

			// instantiation
			$objTextDB = new TextDB('TestTable', array('login', 'password'));

			// after instantiation the textfile exists not, too
			clearstatcache();
			$this -> assertFalse(file_exists($sTextDBDir . 'TestTable.textdb'));

			// store the table and create the text file implicitly
			$objTextDB -> commit();

			// check the file's rights
			if (! $this -> isWindows()) {

				// but on a Windows machine this will not work correctly...
				$this -> assertEqual(fileperms($sTextDBDir . 'TestTable.textdb') & 0777, 0600);
			}

			// after storage the file must exist
			clearstatcache();
			$this -> assertTrue(file_exists($sTextDBDir . 'TestTable.textdb'));

			// drop the table
			$objTextDB -> dropTable();

			// now the original file must not exist, but the dropped one
			clearstatcache();
			$this -> assertFalse(file_exists($sTextDBDir . 'TestTable.textdb'));
			$this -> assertTrue(file_exists($sTextDBDir . 'TestTable.textdb.dropped'));

			// delete file manually
			unlink($sTextDBDir . 'TestTable.textdb.dropped');

			// now the dropped table has gone, too
			clearstatcache();
			$this -> assertFalse(file_exists($sTextDBDir . 'TestTable.textdb.dropped'));
		}


		public function testExceptions() {

			// test instantiation with empty columns array
			try {

				$objTextDB = new TextDB('TestTable2', array());
				$this -> fail();
			}
			catch (InvalidArgumentException $e) {

				$this -> pass();
			}


			// test instantiation with not unique columns array
			try {

				$objTextDB = new TextDB('TestTable2', array('login', 'login', 'password'));
				$this -> fail();
			}
			catch (InvalidArgumentException $e) {

				$this -> pass();
			}


			// get working instance of the TextDB
			$objTextDB = new TextDB('TestTable2', array('login', 'password'));


			// test not existing database
			try {

				$objTextDB -> selectAll();

				// no exception if table is empty or file does not exist
				$this -> pass();
			}
			catch (TextDBException $e) {

				$this -> fail();
			}


			// test correct table structure input
			$objTextDB -> insert(array('login' => 'user1', 'password' => 'password1'));
			$objTextDB -> insert(array('password' => 'password2', 'login' => 'user2'));


			// test wrong table structure input
			try {

				$objTextDB -> insert(array('login' => 'user31', 'password' => 'password3', 'comment' => 'test user'));
				$this -> fail();
			}
			catch (TextDBException $e) {

				$this -> pass();
			}


			// get TextDB directory
			$sTextDBDir = APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'textdb' . DIRECTORY_SEPARATOR;

			// delete database file manually
			unlink($sTextDBDir . 'TestTable2.textdb');
		}


		public function testDB() {

			// get database instance
			$objDB = new TextDB('TestTable3');

			// set the columns
			$objDB -> setColumns(array('login', 'password', 'state'));

			// test empty select
			$this -> assertIdentical($objDB -> selectAll(), array());
			$this -> assertIdentical($objDB -> selectCount(), 0);

			// insert some data
			$objDB -> insert(array('login' => 'user1', 'password' => 'secret1', 'state' => 'active'));

			// get values after insert
			$this -> assertIdentical($objDB -> selectCount(), 1);

			// insert some more data
			$objDB -> insert(array('login' => 'user2', 'password' => 'secret2', 'state' => 'locked'));
			$objDB -> insert(array('login' => 'user3', 'password' => 'secret3', 'state' => 'active'));

			// check values after insert
			$this -> assertIdentical($objDB -> selectCount(), 3);
			$this -> assertIdentical($objDB -> selectWhere('login', 'user1'), array(array('id' => 1, 'login' => 'user1', 'password' => 'secret1', 'state' => 'active')));
			$this -> assertIdentical(count($objDB -> selectWhere('state', 'active')), 2);
			$this -> assertIdentical(count($objDB -> selectWhere('state', 'locked')), 1);

			// password now unique
			$this -> assertIdentical(count($objDB -> selectWhere('password', 'secret2')), 1);

			// modify one line
			$objDB -> updateWhere('login', 'user3', 'password', 'secret2');

			// password now twice available
			$this -> assertIdentical(count($objDB -> selectWhere('password', 'secret2')), 2);

			// delete one line
			$objDB -> deleteWhere('state', 'locked');

			// and check remaining data
			$this -> assertIdentical($objDB -> selectCount(), 2);
			$this -> assertIdentical(count($objDB -> selectWhere('state', 'active')), 2);
			$this -> assertIdentical(count($objDB -> selectWhere('state', 'locked')), 0);

			// delete all
			$objDB -> deleteAll();

			// nothing remains
			$this -> assertIdentical($objDB -> selectCount(), 0);
			$this -> assertIdentical(count($objDB -> selectWhere('state', 'active')), 0);
			$this -> assertIdentical(count($objDB -> selectWhere('state', 'locked')), 0);

			// test transparent handling of \n and \r
			$objDB -> insert(array('login' => 'user_r', 'password' => "foo\rbar", 'state' => 'locked'));
			$objDB -> insert(array('login' => 'user_n', 'password' => "foo\nbar", 'state' => 'locked'));
			$this -> assertIdentical($objDB -> selectCount(), 2);
			$this -> assertIdentical($objDB -> selectWhere('login', 'user_r'), array(array('id' => 4, 'login' => 'user_r', 'password' => "foo\rbar", 'state' => 'locked')));
			$this -> assertIdentical($objDB -> selectWhere('login', 'user_n'), array(array('id' => 5, 'login' => 'user_n', 'password' => "foo\nbar", 'state' => 'locked')));


			// get TextDB directory
			$sTextDBDir = APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'textdb' . DIRECTORY_SEPARATOR;

			// delete database file manually
			unlink($sTextDBDir . 'TestTable3.textdb');
		}


		public function testGettersSetters() {

			// get database instance
			$objDB = new TextDB('TestTable4', array('login', 'password'));



			// test column getter
			$this -> assertIdentical($objDB -> getColumns(), array('login', 'password'));

			// test column setter
			$objDB -> setColumns(array('login', 'password', 'state', 'lastLogin'));
			$this -> assertIdentical($objDB -> getColumns(), array('login', 'password', 'state', 'lastLogin'));



			// test table getter
			$this -> assertIdentical($objDB -> getTable(), 'TestTable4');

			// test table setter
			$objDB -> setTable('TestTable5');
			$this -> assertIdentical($objDB -> getTable(), 'TestTable5');



			// test savepath getter
			$this -> assertIdentical(realpath($objDB -> getSavePath()), realpath(APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'textdb' . DIRECTORY_SEPARATOR));

			// test savepath setter
			$sNewSavePath = realpath(APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'textdb2' . DIRECTORY_SEPARATOR);
			$objDB -> setSavePath($sNewSavePath);
			$this -> assertIdentical($objDB -> getSavePath(), $sNewSavePath);



			// test filename getter
			$this -> assertIdentical(realpath($objDB -> getFileName()), realpath(APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'textdb2' . DIRECTORY_SEPARATOR .'TestTable5.textdb'));

			// test the filename getter again
			$objDB -> setTable('TestTable6');
			$this -> assertIdentical(realpath($objDB -> getFileName()), realpath(APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'textdb2' . DIRECTORY_SEPARATOR .'TestTable6.textdb'));
		}


		public function testAutoCommit() {

			// create simple database
			$objDB = new TextDB('TestTable7', array('login', 'password'));
			$sFileName = $objDB -> getFileName();

			// file must not exist at the beginning
			$this -> assertFalse(file_exists($sFileName));

			// insert some data
			$objDB -> insert(array('login' => 'user1', 'password' => 'password1'));

			// file must not exist at the beginning
			// autocommit is TRUE
			$this -> assertTrue(file_exists($sFileName));

			// store the saved contents
			$sContents = file_get_contents($sFileName);

			// disable autocommit
			$objDB -> setAutoCommit(FALSE);

			// insert some data again
			$objDB -> insert(array('login' => 'user2', 'password' => 'password2'));

			// the contents of the file should not have changed now
			// autocommit is FALSE
			$this -> assertIdentical(file_get_contents($sFileName), $sContents);

			// write the changes to the database
			$objDB -> commit();

			// now the file has changed
			$this -> assertNotIdentical(file_get_contents($sFileName), $sContents);

			// get TextDB directory
			$sTextDBDir = APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'textdb' . DIRECTORY_SEPARATOR;

			// delete database file manually
			unlink($sTextDBDir . 'TestTable7.textdb');
		}
	}



